package com.platform.utils;


import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * RedisUtil工具类
 */
@Component
public class RedisUtils {

    private static final String CACHE_KEY_SEPARATOR = ".";

    @Resource
    private RedisTemplate redisTemplate;

    private DefaultRedisScript<Boolean> casScript;

    public long increment(String key) {
        // increment方法默认增加1，你也可以传递一个long类型的参数来增加指定的值
        return redisTemplate.opsForValue().increment(key, 1);
    }

    public boolean updateKeyTtl(String key, Long time, TimeUnit timeUnit) {
        final long timeoutInMillis = timeUnit.toMillis(time);
        Boolean result = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            // 使用PExpire设置过期时间为毫秒
            // 注意：PExpire需要Redis 2.6.12及以上版本
            return connection.pExpire(key.getBytes(), timeoutInMillis);
        });
        return result != null && result;
    }

    // 加载cas脚本
    // PostConstruct注解用于当依赖注入完成时，立即调用该方法
    @PostConstruct
    public void init() {
        casScript = new DefaultRedisScript<>();
        // 设置redis脚本的预期返回值为boolean
        casScript.setResultType(Boolean.class);
        casScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("compareAndSet.lua")));
    }

    // 调用lua脚本
    public boolean compareAndSet(String key, Long oldValue, Long newValue) {
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return (Boolean) redisTemplate.execute(casScript, keys, oldValue, newValue);
    }

    /**
     * 构建缓存key
     */
    public String buildKey(String... strObjs) {
        return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));
    }

    /**
     * 是否存在key
     */
    public boolean exist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除key
     */
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    // set if not exit，当键不存在时，设置键和值返回true，否则返回false
    public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
    }

    public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, timeUnit);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    public Boolean zAdd(String key, String value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
    }

    public Long countZset(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    public Set<String> rangeZset(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    public Boolean remove(String key) {
        return redisTemplate.delete(key);
    }

    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    public void removeZsetList(String key, Set<String> value) {
        value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));
    }

    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    // 寻找redis中数据类型为set、分数从st到ed的集合
    public Set<String> rangeByScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
    }

    public Object addScore(String key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, obj, score);
    }

    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }

    public Long getExpireTtl(String key, TimeUnit timeUnit) {
        return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
    }
}